﻿using System;
using System.IO;
using System.Json;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace WS.EKA.Utilities.Helpers
{
    public class JsonNetFormatter : MediaTypeFormatter
    {
        public JsonNetFormatter()
        {
            SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
            SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded"));
            MediaTypeMappings.Add(new XHRRequestHeaderMapping());
            Encoding = System.Text.Encoding.UTF8;
        }

        protected override bool CanWriteType(Type type)
        {
            // don't serialize JsonValue structure use default for that
            if (type == typeof(JsonValue) || type == typeof(JsonObject) || type == typeof(JsonArray))
                return false;

            return true;
        }

        protected override bool CanReadType(Type type)
        {
            if (type == typeof(IKeyValueModel))
                return false;

            return true;
        }

        protected override System.Threading.Tasks.Task<object> OnReadFromStreamAsync(Type type, System.IO.Stream stream, System.Net.Http.Headers.HttpContentHeaders contentHeaders, FormatterContext formatterContext)
        {
            var task = Task<object>.Factory.StartNew(() =>
            {
                var settings = new JsonSerializerSettings()
                {
                    NullValueHandling = NullValueHandling.Ignore,
                };

                var sr = new StreamReader(stream);
                var jreader = new JsonTextReader(sr);

                var ser = new JsonSerializer();
                ser.Converters.Add(new IsoDateTimeConverter());

                object val = ser.Deserialize(jreader, type);
                return val;
            });

            return task;
        }

        protected override System.Threading.Tasks.Task OnWriteToStreamAsync(Type type, object value, System.IO.Stream stream, System.Net.Http.Headers.HttpContentHeaders contentHeaders, FormatterContext formatterContext, System.Net.TransportContext transportContext)
        {
            var task = Task.Factory.StartNew(() =>
            {
                var settings = new JsonSerializerSettings()
                {
                    NullValueHandling = NullValueHandling.Ignore,
                };

                string json = JsonConvert.SerializeObject(value,
                    Formatting.Indented,
                    new JsonConverter[1] 
                    { 
                        new IsoDateTimeConverter()
                         {
                          DateTimeFormat="yyyy'/'MM'/'dd' 'HH':'mm':'ss"
                          }
                    }
                    );
                byte[] buf = Encoding.GetBytes(json);
                stream.Write(buf, 0, buf.Length);
                stream.Flush();
            });

            return task;
        }
    }

    class XHRRequestHeaderMapping : RequestHeaderMapping
    {
        public XHRRequestHeaderMapping()
            : base("x-requested-with", "xmlhttprequest", StringComparison.OrdinalIgnoreCase, true, "application/json")
        {
        }


        protected override double OnTryMatchMediaType(HttpRequestMessage request)
        {
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if ((request.Headers.Accept != null) && ((request.Headers.Accept.Count != 1) || !request.Headers.Accept.First<MediaTypeWithQualityHeaderValue>().MediaType.Equals("*/*", StringComparison.Ordinal)))
            {
                return 0.0;
            }
            return base.OnTryMatchMediaType(request);
        }

        protected override double OnTryMatchMediaType(HttpResponseMessage response)
        {
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }
            if (response.RequestMessage != null)
            {
                return this.OnTryMatchMediaType(response.RequestMessage);
            }
            return 0.0;
        }
    }
}